#!/usr/bin/env python

# Copyright (c) David Townshend
#
# This program is free software; you can redistribute it and/or modify it 
# under the terms of the GNU General Public License as published by the 
# Free Software Foundation; either version 2 of the License, or (at your 
# option) any later version.

# This program is distributed in the hope that it will be useful, but 
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
# for more details. 

# You should have received a copy of the GNU General Public License along 
# with this program; if not, write to the Free Software Foundation, Inc., 
# 675 Mass Ave, Cambridge, MA 02139, USA.

__version__ = '0.1.0'

import struct
import collections
import os
import datetime

_test_data = b'''ZH\xc3\x1eb\xc3\x00\x00\x03\x00\x00\x00\x02\x00\x00\x00\x11\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00SCHI2\x05\x00\x00\x00SCHI1\x03\x00\x00\x00J13\x03\x00\x00\x00J16\x03\x00\x00\x00J12\x03\x00\x00\x00J11\x03\x00\x00\x00J15\x03\x00\x00\x00J14\x03\x00\x00\x00J10\x02\x00\x00\x00J9\x02\x00\x00\x00J7\x02\x00\x00\x00J8\x02\x00\x00\x00J4\x02\x00\x00\x00J3\x02\x00\x00\x00J2\x02\x00\x00\x00J1\x06\x00\x00\x00C8H036\x07\x00\x00\x00Outfall\n\x00\x00\x00Saulspoort\x02\x00\x00\x00C7\x02\x00\x00\x00C5\x02\x00\x00\x00C6\x03\x00\x00\x00C16\x02\x00\x00\x00C1\x02\x00\x00\x00C2\x02\x00\x00\x00C4\x02\x00\x00\x00C3\x02\x00\x00\x00C9\x03\x00\x00\x00C10\x03\x00\x00\x00C11\x03\x00\x00\x00C12\x03\x00\x00\x00C13\x03\x00\x00\x00C14\x03\x00\x00\x00C15\x02\x00\x00\x00W1\x01\x00\x00\x00\x01\x00\x00\x00\x00PCG\x00PCG\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\xa0@\x00\x00\x00\x00\x00 \xfaD\x00\x00\xa0@\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\xa0@\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\xa0@\x00\x00\x00\x00\x00P\x11E\x00\x00\xa0@\x00\x00\x00\x00\x00\x10\nE\x00\x00\xa0@\x00\x00\x00\x00\x00 \tE\x00\x00\xa0@\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\xa0@\x00\x00\x00\x00\x00p\x07E\x00\x00\xa0@\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\xa0@\x00\x00\x00\x00\xe6"\xccD\x00\x00\xa0@\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\xa0@\x00\x00\x00\x00\x00@\xceD\x00\x00\xa0@\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\xa0@\x00\x00\x00\x00\x00\xa0\xd8D\x00\x00\xa0@\x01\x00\x00\x00\x00\x80\xcbD\xb8\x1emB\x02\x00\x00\x00\x00@\xcaD\x00\x00@A\x05\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\xf6\x86\x90E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@)\xc4\xe3E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@f\x1a\xb5E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\x85\x94\x90F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@{\xff\x0bF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\n\x81\x89F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@{`\x06F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\x9f\xfc\x83F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\xa4\xcb2F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\xe1\x0b=F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@\xec\xe3\xe1E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@WK\xc2F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@f\xaepE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@=;uF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0@=+\xc5F\x03\x00\x00\x00\xe1z\x14A\xe1z\x14A\x00\x00HB\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00\x0b\x00\x00\x00\x0c\x00\x00\x00\r\x00\x00\x00\x00\x00\x00\x00`\xbd\xe2@0*\x00\x00\x00\x00\x00\x00d\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xef\x8cH?\xf8;\xccD\x00\x00\x00\x00\x00\x00\x00\x00Y%nA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbdwO?\xefY\xceD\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x8c{A\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:=$?\x88\xb4\xd8D\x00\x00\x00\x00w\xbeoAw\xbeoA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00\xe7\x01#C\x00\x00\x00\x00\xb5\xb8"Aq\x85\xcbD\xc5\xb2\x94K\x00\x00\x00\x00\xde\x04qA\x00\x00\x00\x00\xde\x04qAL"5?;y~@\x93i\xc7?=\xe8\x10>Y%nAV\x02L?"f]@\xe4\x11\xa4?\x125#>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x8c{A\xf2\xb6&?\x1a\x0b\x91@\xa9e\xec?(_\x05>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe7\x01#C\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00w\xbeoAw\xbeoA\x00\x00\x00\x00\xe7\x01#C\x9e\x8f\x95K\x00\x00\x00\x00\x00\x00\x00\x00h\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xeeCM?\x8e<\xccD\x00\x00\x00\x00\x00\x00\x00\x00(@wA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd5I\\?\x89[\xceD\x00\x00\x00\x00\x00\x00\x00\x00\x96\x8d\x8aA\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\\\xef:?^\xb7\xd8D\x00\x00\x00\x00\xb4\xc8\x93A\xb4\xc8\x93A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00%;\xbdB\x00\x00\x00\x00\xaaa\x1eA\xc3|\xcbD5\xb3\x8bK\x00\x00\x00\x00N\x04uA\x00\x00\x00\x00N\x04uA\xc9.8?5!~@}\x95\xc5?\xa1X\x13>(@wA\xe2\xc6T?\xd8\xb1[@r\xa2\x9f?\xb48*>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x96\x8d\x8aAYG7?uv\x90@\x84(\xe1?{\x9f\x12>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%;\xbdB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb4\xc8\x93A\xb4\xc8\x93A\x00\x00\x00\x00%;\xbdB\xcd\xa0\x8cK\x00\x00\x00\x00\x00\x00\x00\x00l\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa8\x0c\x81?)C\xccD\x00\x00\x00\x00\x00\x00\x00\x00$\xe1\xb2A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcb\xa4\x86?\xa9a\xceD\x00\x00\x00\x00\x00\x00\x00\x00\x85\x8b\xbfA\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8f\xa3_?\xf4\xbb\xd8D\x00\x00\x00\x00Zd\xc5AZd\xc5A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00QsyB\x00\x00\x00\x00\xb8\xfb\x1bA\xf7w\xcbD\xfc\xf5\x86K\x00\x00\x00\x00;W\xb1A\x00\x00\x00\x00;W\xb1A\xb3\x93g?\x06\xcf\x8f@G\xe7\xc8?\xf6B9>$\xe1\xb2A\xba\xd8\x83?\x92\xe1{@\xe0\xab\xa5?\\\xf4R>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x85\x8b\xbfA\x00\x93]?L\xec\xa2@2U\xe8?fB1>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00QsyB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Zd\xc5AZd\xc5A\x00\x00\x00\x00QsyBg\x1d\x88K\x00\x00\x00\x00\x00\x00\x00\x00p\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00f\x97\x88?\x0cE\xccD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\xc4A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04\xde\x8d?xc\xceD\x00\x00\x00\x00\x00\x00\x00\x00ca\xd0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc4>`?\x08\xbc\xd8D\x00\x00\x00\x00\x89A\xc6A\x89A\xc6A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00w\xa6=B\x00\x00\x00\x00\xdf\xb9\x1aAtu\xcbD\xcc\x8c\x84K\x00\x00\x00\x00\xa8\x8b\xc6A\x00\x00\x00\x00\xa8\x8b\xc6A\xc7\xbcv?\x8dK\x96@\x83\xe0\xcb?\xd2cE>\x00\x08\xc4A\xb5:\x8b?\x9d\x04\x82@\x1e\xcc\xa6?U\xc4^>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00ca\xd0A\xd8\xc2c?\xa6\x0b\xac@\x1e7\xf2?z56>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00w\xa6=B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x89A\xc6A\x89A\xc6A\x00\x00\x00\x00w\xa6=B\x80\xc1\x85K\x00\x00\x00\x00\x00\x00\x00\x00t\xbd\xe2@)\\\xe9A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00q=VA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xd1D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00r\xef\x88?"E\xccD\x00\x00\x00\x00\x00\x00\x00\x00\xd9\xd3\xc4A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00i\x9f\x88?(b\xceD\x00\x00\x00\x00\x00\x00\x00\x00\x89\x1a\xc4A\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00W\xb3U?\xb6\xba\xd8D\x00\x00\x00\x00\x98n\xb7A\x98n\xb7A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0\x1cB\x00\x00\x00\x00\xb7\xfc\x19A\xf9s\xcbD\x1b(\x83K\x00\x00\x00\x00\xad\x1a\xcaA\x00\x00\x00\x00\xad\x1a\xcaAAkx?\xc6\xdc\x97@,[\xcd?4\xbcF>\xd9\xd3\xc4An\xc7\x88?Y\x1d\x85@\x9b+\xac?\xb0\xd8Z>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x89\x1a\xc4A\x7f7Z?Q\x92\xa9@\x96\x8a\xf3?\xcc\x92.>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0\x1cB\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8Aq=\xaaA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98n\xb7A\x98n\xb7A\x00\x00\x00\x00\xff\xe0\x1cB\x98S\x84K\x00\x00\x00\x00\x00\x00\x00\x00x\xbd\xe2@\\\x8fB?\x00\x00\x00\x00\xbc$GAw\x12LC\x00\x00\x00\x00\x00\x00\x00\x00R\xb8\x9e>\x00\x00\x00\x00m1>A+_r@\x00\x00\x00\x00\x00\x00\x00\x00l\x9bq@\xce\xf8\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\xa5\xb43C\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\xdd]@\xefN\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\xe9\xb5:C\x00\x00\x00\x00=\x89@@E\xe0\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\xd6\xf8DC\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf9\\\xcf?\xec9\tE\x00\x00\x00\x00w\x12LCw\x12LC\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\x9e\x8d?NF\xccD\x00\x00\x00\x00\x00\x00\x00\x00%\xcb\xcfA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000e\x8f?\xd9c\xceD\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x02\xd4A\x00\x00\x00\x00\x06\xcc\x12>K\xe2\x01E\x00\x00\x00\x00+_r@+_r@\x00\x00\x00\x00\xd6Bg?\xe8\xbc\xd8D\x00\x00\x00\x00\'1\xb5Aq[\xd0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00\xab~5B\x00\x00\x00\x00P\x91\x1aA#u\xcbD\xf0?\x84K\x00\x00\x00\x00y\x00\x0eC\x00\x00\x00\x00\xe6n\xceA\xfdI~?\xec7\x97@\xb0I\xca?dnK>%\xcb\xcfA\xdc\x81\x8e?\xc9V\x86@\x16\x82\xaa?\xf9\x02d>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa9\x02\xd4A\x91\x83h?3,\xab@\xd8\xae\xee?\xda\x02:>NRY@7\x1a\x0e>Xq\x9a@W=\x85@%]\xe3<\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd6\xf8DC\x8f\x1e\xcd?\xd6{\xa9A\x19^\xb6@s\x18\xa4>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe9\xb5:C\x1a\x7f=@m\xac\x1bAT\x07\x00@\xe1\x98\x17?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa5\xb43C+a[@\x88\xd8\xf9@~\x0b\xc1?\xef\x80/?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009e\xe8B\x0e\rV@\xe2\x9a\xa6@R\x14\x82?\xa4=+?\xab~5B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\xc3\xf5\x08?\x00\x00\x00\x00\x14\xabBA\xf4\xdbOC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\'1\xb5A\x19\x82fC\x00\x00\x00\x00\xab~5BEe\x8cK\x00\x00\x00\x00\x00\x00\x00\x00|\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00q8MA.\xc0sA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\xc1\xe6?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x003C\xd5?Q\xb5\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\x93\xf39B\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00l\xe0\xaa?\xb8\n\xd2D\x00\x00\x00\x00\x00\x00\x00\x00,\x10\x1cB\x00\x00\x00\x00\x91\xb8a?7\x9c\xd6D\x00\x00\x00\x00\x00\x00\x00\x00i\r\xd6A\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6\x0f\xa7>8%\tE\x00\x00\x00\x00.\xc0sA.\xc0sA\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbf\xf2\x82?\xa3C\xccD\x00\x00\x00\x00\x00\x00\x00\x00;$\xb7A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0#\x89?Ib\xceD\x00\x00\x00\x00\x00\x00\x00\x00cM\xc5A\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x895k?g\xbd\xd8D\x00\x00\x00\x00V\x0e\xcfA\xc2\x1f\xd6A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00Q\x01\xc5B\x00\x00\x00\x008\xae\x1eA\\}\xcbD\xcaM\x8cK\x00\x00\x00\x00\xd5\x0c\xecB\x00\x00\x00\x00\xce\x1e\xb3Ad\x00j?>\x9f\x8f@\x80\xac\xc7?\x833;>;$\xb7AO\x0b\x86?\xafB}@]P\xa5?\x7fxV>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00cM\xc5A|de?]\xa5\xa1@\xe1\xd1\xe2?\x96\x837>\x88-b?\xd0\xb8\xf3<\x0by\xbd@3\x94/A@\xfa\xc2;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00i\r\xd6A\xb8\x87\xc9>\xa7CQA\xee\xee\xd8@`9\xa1=\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x10\x1cB\x90w\x7f?\x06~\xe3@\xef\xd5\x17@\xa6_L>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x93\xf39BI\xab\xb4?\xf5\xb1\xb8@\xf2h\xd2?\x07\x89\x90>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"E\xbfB@;\x08@\xcb\x0c\xed@{\xeb\xe0?g\xf8\xd9>Q\x01\xc5B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\x95\x10\xea@.\xc0sA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00V\x0e\xcfA7w$B\x00\x00\x00\x00Q\x01\xc5B\xe5\xb9\x90K\x00\x00\x00\x00\x00\x00\x00\x00\x80\xbd\xe2@\x00\x00\x00\x00\x00\x00\x00\x00\xc9\xd7Z@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9c2\xd9>\x93\x8d\xd0D\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xd4\xa2@\x00\x00\x00\x00\x00\x00\x00\x00\x00 \xfaD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xad\xb4\xa3>;\xea\xd1D\x00\x00\x00\x00\x00\x00\x00\x00H\x89v@\x00\x00\x00\x00\xc3\xaba>\r\x87\xd6D\x00\x00\x00\x00\x00\x00\x00\x00\xe0T3@\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x11E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\nE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \tE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x0fE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x07E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\xd2D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13_j?2@\xccD\x00\x00\x00\x00\x00\x00\x00\x00\x86\x1e\x99A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd8D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\xdbm?\xbb]\xceD\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xce\x9cA\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x01E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x0e0?\x02\xb6\xd8D\x00\x00\x00\x00\x00\x00\x86A\x8f!\x86A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\xcbD\x00\x00\x00\x00\x00\x00\x00\x00&\x89\xa2B\x00\x00\x00\x00\xa1n\x1dA\xddz\xcbDQ\xcd\x89K\x00\x00\x00\x00\n\x90\x03B\x00\x00\x00\x00I\xcb\x9eA\x92DU?0\xbf\x8c@\xa0Q\xcc?B\x9d*>\x86\x1e\x99A\x1a\x1dl?\xdb*s@\x9bT\xa8?\x15\xe4<>\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\xce\x9cA\xc1\xfc8?z\xe2\xa1@`6\xfb?g\xfd\x13>\xdf;\x86<\x05z1;\x00\x00\x00\x00\x00\x00\x00\x007\xfb\r:\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0T3@J\xacn=\xcf\xf8\x18A\xa9\xddJA;\xf0><\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00H\x89v@\xe8\xd2y>\x07EE@h\xfe\x00@\xed\xdbG=\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf1\xd4\xa2@/\xeb\xb2>\x9b\x124@x\xb9\xc5?\x8c"\x8f=\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\xa9PA\xf2R\x17?\x19N\x85@0y\xe3?P\x1e\xf2=&\x89\xa2B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8e\xe3\xa8A\x00\x00\x00\x00\x00\x00\x00\x00\xca\xd7\xda?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x86A\x00\x00\x86A\x00\x00\x00\x00&\x89\xa2B\xf1\x8a\x8bK\x00\x00\x00\x00\x1c\x00\x00\x00\x13\x01\x00\x00\xef\x03\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00ZH\xc3\x1e'''

class OutFile:
    '''Provide and interface to a SWMM .out file.
    
    The `OutFile` class has methods and properties to allow querying data in a 
    binary SWMM .out file. A typical way of using this by specifying an open
    file in the constructor, and using `get_values` to query data. For example:
    
    >>> import io
    >>> swmmfile = io.BytesIO(_test_data)
    >>> outfile = OutFile(swmmfile)
    >>> outfile.names['nodes']
    ('J13', 'J16', 'J12', 'J11', 'J15', 'J14', 'J10', 'J9', 'J7', 'J8', 'J4', 'J3', 'J2', 'J1', 'C8H036', 'Outfall', 'Saulspoort')
    >>> outfile.variables['nodes'][0]
    'depth'
    >>> outfile.start
    datetime.datetime(2005, 1, 29, 0, 0)
    >>> outfile.get_values('nodes', ['J13', 'J15'], ['depth'], timesteps=3)  # doctest: +NORMALIZE_WHITESPACE 
    [(datetime.datetime(2005, 1, 29, 3, 0), ('J13', 0.0), ('J15', 0.0)), 
     (datetime.datetime(2005, 1, 29, 6, 0), ('J13', 0.0), ('J15', 0.0)), 
     (datetime.datetime(2005, 1, 29, 9, 0), ('J13', 0.0), ('J15', 0.0))]
    '''

    # Named tuple containing information about each object in the SWMM model.
    # These are the type specifications.
    Subcatchment = collections.namedtuple('Subcatchments',
        'name area')
    Node = collections.namedtuple('Node',
        'name type invert max_depth')
    Link = collections.namedtuple('Link',
        'name type in_offset out_offset max_depth length')
    Pollutant = collections.namedtuple('Pollutant',
        'name units')

    def __init__(self, fh):
        ''' Initialise the OutFile instance with an open file object. Some
        information is read from the file at this stage too, namely some 
        metadata (e.g. start time, number of objects, etc), the object names,
        the reported variable names, and the object info. No timeseries data is
        read here.
        '''
        # The opened file object
        self._fh = fh
        # Set up some lists
        self.names = {'subcatchments': [], 'nodes': [], 'links': [], 'pollutants': []}
        self.variables = {'subcatchments': [], 'nodes': [], 'links': [], 'system': []}
        self.info = {'subcatchments': [], 'nodes': [], 'links': [], 'pollutants': []}
        # Object counts
        self.count_cats = 0
        self.count_nodes = 0
        self.count_links = 0
        self.count_timesteps = 0
        self.count_pollutants = 0
        # Object variable counts
        self.count_vars_cats = 0
        self.count_vars_nodes = 0
        self.count_vars_links = 0
        self.count_vars_system = 14
        # Start time (a datetime object)
        self.start = None
        # Duration of each timestep (a timedelta object)
        self.timestep = None
        # Error code
        self.error_code = 0
        # Flow units used, e.g. CFS, CMS, etc
        self.units = ''
        # Important positions within the file
        self._pos_names = 0
        self._pos_props = 0
        self._pos_results = 0
        # Read some inital data from the file
        self._parse_header()
        self._parse_names()

    def close(self):
        ''' Close the file.'''
        self._fh.close()
        self._fh = None

    def __del__(self):
        self.close()

    def _get_var_values(self, group, variables=None):
        '''Return a tuple of values. If variables is None, then all are 
        returned. This function reads from the current position in the file
        and when complete leaves the pointer at the end of the last available
        variables (whether it was read or not).
        '''
        ret = []
        for name in self.variables[group]:
            value = self.getfloat()
            if variables is None or name in variables:
                ret.append(value)
        return ret

    def _get_name_values(self, group, sz_row, names=None, variables=None):
        '''Return a list of tuples of values. This behaves similar to 
        _get_var_values, which it calls. The return value is a list in the 
        following format:
        [(nameA, varA1, varA2, ...), (nameB, varB1,...), ...]
        sz_row is the byte size of a row
        '''
        ret = []
        if group == 'system':
            return [tuple(['system'] + self._get_var_values(group, variables))]
        for name in self.names[group]:
            values = tuple([name] + self._get_var_values(group, variables))
            if names is None or name in names:
                ret.append(values)
        return ret

    def _calc_results_positions(self, group, start=None):
        '''Calculate various positions and sizes in the file of results.'''
        # If start is missing, use self.start
        if start is None: start = self.start
        # Size of individual records
        sz_cat = self.count_vars_cats * 4
        sz_node = self.count_vars_nodes * 4
        sz_link = self.count_vars_links * 4
        sz_system = self.count_vars_system * 4
        # Size of sections
        sz_sec_cats = sz_cat * self.count_cats
        sz_sec_nodes = sz_node * self.count_nodes
        sz_sec_links = sz_link * self.count_links
        # Size of an entire timestep
        sz_timestep = 8 + sz_sec_cats + sz_sec_nodes + sz_sec_links + sz_system
        # Distance to jump between date and relevant group
        # sz_jump1 is the distance between the date and the group
        # sz_jump2 is the distance between the end of the group and the next date
        if group == 'subcatchments':
            sz_jump1 = 0
            sz_jump2 = sz_sec_nodes + sz_sec_links + sz_system
            sz_row = sz_cat
        elif group == 'nodes':
            sz_jump1 = sz_sec_cats
            sz_jump2 = sz_sec_links + sz_system
            sz_row = sz_node
        elif group == 'links':
            sz_jump1 = sz_sec_cats + sz_sec_nodes
            sz_jump2 = sz_system
            sz_row = sz_link
        elif group == 'system':
            sz_jump1 = sz_sec_cats + sz_sec_nodes + sz_sec_links
            sz_jump2 = 0
            sz_row = 0
        # Number of timesteps to start
        ts_start = int((self.start - start).total_seconds() /
                       self.timestep.total_seconds())
        # Position of the start date
        pos_start = self._pos_results + (ts_start * sz_timestep)
        return pos_start, sz_jump1, sz_jump2, sz_row

    def get_values(self, group, names=None, variables=None, start=None, timesteps=None):
        ''' Return a list filtered list of values. Output is as a list of 
        tuples in the following format:
        (datetime, name, variable1, variable2, ...)
        The list is filtered so that only dates between start and end 
        (inclusive) are in the output.
        
        group is one of 'subcatchments', 'nodes', 'links' or 'system'
        '''
        if timesteps is None:
            timesteps = self.count_timesteps
        assert timesteps <= self.count_timesteps
        assert self.count_cats == len(self.names['subcatchments']), (self.count_cats, self.names['subcatchments'])
        ret = []
        # Calculate various sizes
        pos_start, sz_jump1, sz_jump2, sz_row = self._calc_results_positions(group, start)
        # Move to the first required time
        self._fh.seek(pos_start)
        # Get date
        for i in range(timesteps):
            date = self.getdate()
            # Jump forward to relevant section
            self._fh.seek(sz_jump1, os.SEEK_CUR)
            values = self._get_name_values(group, sz_row, names, variables)
            ret.append(tuple([date] + values))
            # Jump to next date
            self._fh.seek(sz_jump2, os.SEEK_CUR)
        return ret

    def _parse_header(self):
        # Read the header information as follows:
        # - an identifying number equal to 516114522  
        # - the version number of the engine (currently 50005)  
        # - a code number for the flow units that are in effect where 
        #   0 = CFS, 1 = GPM, 2 = MGD, 3 = CMS, 4 = LPS, and 5 = LPD  
        # - the number of subcatchments in the project reported on  
        # - the number of nodes in the project reported on  
        # - the number of links in the project reported on  
        # - the number of pollutants in the project.        
        self._fh.seek(0)
        # Check swmm file ID
        assert self.getint() == 516114522
        # Check swmm version
        assert self.getint() > 50000
        # Check units
        self.units = ['CFS', 'GPM', 'MGD', 'CMS', 'LPS', 'LPD'][self.getint()]
        # Get count of entities
        self.count_cats = self.getint()
        self.count_nodes = self.getint()
        self.count_links = self.getint()
        self.count_pollutants = self.getint()
        self.count_vars_cats = 6 + self.count_pollutants
        self.count_vars_nodes = 6 + self.count_pollutants
        self.count_vars_links = 5 + self.count_pollutants

        # Read the footer information as follows:
        # - the byte position where the Object ID Names section begins 
        # - the byte position where the Object Properties section begins 
        # - the byte position where the Computed Results section begins 
        # - the total number of reporting periods in Computed Results
        # - the error code status of the simulation, where 0 indicates no errors
        # - the id number, 516114522, as in the very first record in the file
        self._fh.seek(-24, os.SEEK_END)
        self._pos_names = self.getint()
        self._pos_props = self.getint()
        self._pos_results = self.getint()
        self.count_timesteps = self.getint()
        self.error_code = self.getint()
        assert self.getint() == 516114522

    def _parse_names(self):
        ''' Read the names, info and variables for each subcatchment, node, 
        link, and pollutant. 
        '''
        # Define the string values of the codes in the file
        node_codes = ['Junction', 'Outfall', 'Storage', 'Divider']
        link_codes = ['Conduit', 'Pump', 'Orifice', 'Weir', 'Outlet']
        pollutant_codes = ['mg/L', 'ug/L', 'counts/L']

        # Move to the start position of the names section
        self._fh.seek(self._pos_names)

        # Get names
        self.names['subcatchments'] = self.getstr(self.count_cats)
        self.names['nodes'] = self.getstr(self.count_nodes)
        self.names['links'] = self.getstr(self.count_links)
        self.names['pollutants'] = self.getstr(self.count_pollutants)

        # Get pollutant codes
        units = self.getint(self.count_pollutants)
        units = [pollutant_codes[u] for u in units]
        self.info['pollutants'] = [
            self.Pollutant(n, u) for n, u in zip(self.names['pollutants'], units)]

        #Get info for the other groups
        # Subcatchments
        assert self.getint(2) == (1, 1)  # No of properties and code
        for i in range(self.count_cats):
            name = self.names['subcatchments'][i]
            area = self.getfloat()
            self.info['subcatchments'].append(self.Subcatchment(name, area))
        # Nodes
        assert self.getint(4) == (3, 0, 2, 3) # No of properties and codes
        for i in range(self.count_nodes):
            name = self.names['nodes'][i]
            type, invert, depth = self.getfloat(3)
            type = node_codes[int(type)]
            self.info['nodes'].append(self.Node(name, type, invert, depth))
        # Links
        assert self.getint(6) == (5, 0, 4, 4, 3, 5)  # No of properties and codes
        for i in range(self.count_links):
            name = self.names['links'][i]
            type, offset1, offset2, depth, length = self.getfloat(5)
            type = link_codes[int(type)]
            self.info['links'].append(self.Link(name, type, offset1, offset2, depth, length))

        # Define the reporting variables. Except fot the pollutant 
        # concentrations, they are hard coded since they do not change.
        # The pollutant concentrations are 'conc pollutant_name', e.g.
        # if there is a pollutant named 'TSS', the concentration variable
        # will be 'conc TSS'
        self.variables['subcatchments'] = ['rainfall',
                                          'snow depth',
                                          'losses',
                                          'runoff',
                                          'groundwater flow',
                                          'groundwater elevation']
        self.variables['nodes'] = ['depth',
                                   'head',
                                   'storage',
                                   'lateral inflow',
                                   'total inflow',
                                   'flooding']
        self.variables['links'] = ['flow',
                                   'depth',
                                   'velocity',
                                   'Froude',
                                   'capacity']
        self.variables['system'] = ['temperature',
                                    'rainfall',
                                    'snow depth',
                                    'losses',
                                    'runoff',
                                    'dry weather inflow',
                                    'groundwater inflow',
                                    'RDII inflow',
                                    'direct inflow',
                                    'total inflow',
                                    'flooding',
                                    'outflow',
                                    'storage',
                                    'evaporation']
        for p in self.names['pollutants']:
            for vars in ['subcatchments', 'nodes', 'links']:
                self.variables[vars].append('conc {0}'.format(p))

        # Get reporting periods
        for g in ['subcatchments', 'nodes', 'links', 'system']:
            assert self.getint() == len(self.variables[g])
            vals = self.getint(len(self.variables[g]))
            assert vals == tuple(range(len(self.variables[g]))), (g, vals)

#        sz_vars = sum([len(v) for v in self.variables.itervalues()])       
#        self._fh.seek(sz_vars, os.SEEK_CUR)
        self.start = self.getdate()
        self.timestep = datetime.timedelta(seconds=self.getint())

    def getint(self, count=1):
        ''' Read count number of 4-byte integer from a binary file.'''
        if count < 1:
            return tuple()
        val = struct.unpack('i' * count, self._fh.read(4 * count))
        return val if count > 1 else val[0]

    def getfloat(self, count=1):
        ''' Read count number of 4-byte float from a binary file.'''
        if count < 1:
            return tuple()
        val = struct.unpack('f' * count, self._fh.read(4 * count))
        return val if count > 1 else val[0]

    def getdouble(self, count=1):
        ''' Read count number of 8-byte double from a binary file.'''
        if count < 1:
            return tuple()
        val = struct.unpack('d' * count, self._fh.read(8 * count))
        return val if count > 1 else val[0]

    def getdate(self, count=1):
        if count < 1:
            return tuple()
        ret = tuple()
        for i in range(count):
            days = self.getdouble()
            assert days > 0, days
            delta = datetime.timedelta(days=days-2)
            ret += (datetime.datetime(1900, 1, 1) + delta,)
        return ret if len(ret) > 1 else ret[0]

    def getstr(self, count=1):
        ''' Read count number of strings of length from a binary file.'''
        if count < 1:
            return tuple()
        ret = tuple()
        for i in range(count):
            ln = self.getint()
            assert ln >= 0
            if ln == 0:
                ret += ('',)
                # An empty string still contains 1 byte, so jump a byte
                self._fh.seek(1, os.SEEK_CUR)
            else:
                strings = struct.unpack('s' * ln, self._fh.read(ln))
                strings = [s.decode() for s in strings]
                ret += (''.join(strings),)
        return ret #if len(ret) > 1 else ret[0]

# Replace the builtin open function
__builtin_open = open

def open(filename):
    ''' Open a SWMM .out file and return an `~swmmout.OutFile` instance. '''
    fh = __builtin_open(filename, 'rb')
    return OutFile(fh)


################################################################################
## TESTING FUNCTIONS
################################################################################

def test():
    import unittest
    from mock import Mock

    class TestOutFile(unittest.TestCase):

        def assertAlmostEqual(self, a, b, places=7):
            if isinstance(a, list) or isinstance(a, tuple):
                self.assertEqual(len(a), len(b))
                self.assertEqual(type(a), type(b))
                for ea, eb in zip(a, b):
                    super(TestOutFile, self).assertAlmostEqual(ea, eb, places)
            else:
                super(TestOutFile, self).assertAlmostEqual(a, b, places)

        def setUp(self):
            # Create a mock "self" instance of OutFile
            self.slf = Mock(OutFile)
            self.slf._fh = Mock()

        def tearDown(self):
            pass

        def test_getint(self):
            # Test a single int by first converting to and then from
            func = OutFile.getint.im_func
            tests = [(267, 0, -80), 3]
            for test in tests:
                tup = test if isinstance(test, tuple) else (test,)
                string = struct.pack('{}i'.format(len(tup)), *tup)
                self.slf._fh.read = Mock(return_value=string)
                self.assertEqual(func(self.slf, len(tup)), test)

        def test_getfloat(self):
            # Test a single float by first converting to and then from
            func = OutFile.getfloat.im_func
            tests = [(267.217343254356, 0.000000009, -80.), 3.2]
            for test in tests:
                tup = test if isinstance(test, tuple) else (test,)
                string = struct.pack('{}f'.format(len(tup)), *tup)
                self.slf._fh.read = Mock(return_value=string)
                self.assertAlmostEqual(func(self.slf, len(tup)), test, 5)

        def test_getdouble(self):
            # Test a single float by first converting to and then from
            func = OutFile.getdouble.im_func
            tests = [(267.2143465, 0.000000009, -80.), 3.2, 5.23789e809]
            for test in tests:
                tup = test if isinstance(test, tuple) else (test,)
                string = struct.pack('{}d'.format(len(tup)), *tup)
                self.slf._fh.read = Mock(return_value=string)
                self.assertAlmostEqual(func(self.slf, len(tup)), test)

        def test_all(self):
            f = open('test.out')
            self.assertTrue(isinstance(f, OutFile))
            self.assertEqual(f.count_cats, 2)
            self.assertEqual(f.count_nodes, 17)
            self.assertEqual(f.count_links, 16)
            self.assertEqual(str(f.start), '2005-01-29 00:00:00')
            self.assertEqual(str(f.timestep), '3:00:00')

    suite = unittest.TestLoader().loadTestsFromTestCase(TestOutFile)
    unittest.TextTestRunner(verbosity=2).run(suite)

if __name__ == '__main__':
   # test()
    f = open('StorageEx.out')
    print(f.count_cats, f.count_nodes, f.count_links)
    print(f.start, f.timestep)
    print(f.get_values('subcatchments'))
    print(f.get_values('nodes'))
    print(f.get_values('links'))
    print(f.get_values('system'))